﻿using System;
using System.Linq;
using System.Collections.Generic;
using AbstractBugTracker;
using AbstractProducts;
using SmartAssembly;
using Utilities;

namespace Engine.Buggets
{
    internal sealed class SecondaryCreator<TBug, TBugDef> : IBugget<TBug>
        where TBug : class, IBug
        where TBugDef : BugDef
    {
        private readonly IBugPresenter<TBug, TBugDef> m_BugPresenter;
        private readonly IBugTracker<TBug, TBugDef> m_BugTracker;

        internal SecondaryCreator(IBugPresenter<TBug, TBugDef> bugPresenter, IBugTracker<TBug, TBugDef> bugTracker)
        {
            m_BugPresenter = bugPresenter;
            m_BugTracker = bugTracker;
        }

        public IEnumerable<Tuple<ProcessedReport, TBug>> Run(ProcessedReport processedReport1, TBug bug1)
        {
            yield return new Tuple<ProcessedReport, TBug>(processedReport1, bug1);

            string assemblyName = processedReport1.Report.ShortAssemblyNameOfMethodTypeOrNull;
            if (assemblyName == null)
            {
                yield break;
            }

            IEnumerable<ProductDependency> productDependencies = m_BugTracker.GetProductDependencies(processedReport1.ProductBuildRef.ProductRef);
            foreach (ProductDependency productDependency in productDependencies)
            {
                if (!productDependency.AssemblyRefs.Contains(assemblyName))
                {
                    continue;
                }

                Version productBuildVersion = processedReport1.Report.GetProperAssemblyName(assemblyName).Version;
                ProductBuildRef productBuildRef = new ProductBuildRef(productDependency.DependentProductRef, productBuildVersion);
                ProcessedReport processedReport2 = processedReport1.Blame(productBuildRef);
                foreach(Tuple<ProcessedReport, TBug> eachTuple in SearchForOrCreateBugs(processedReport2, bug1))
                {
                    yield return eachTuple;
                }
            }
        }

        /// <remarks>
        /// This is partially duplicated in <see cref="NormalCreator{TBug,TBugDef}"/>
        /// </remarks>
        private IEnumerable<Tuple<ProcessedReport, TBug>> SearchForOrCreateBugs(ProcessedReport processedReport, TBug effectBug)
        {
            ProductRef productRef = processedReport.ProductBuildRef.ProductRef;
            IExceptionReportHasher exceptionReportHasher = m_BugTracker.GetExceptionReportHasher(productRef);
            IExceptionReportMapper exceptionReportMapper = m_BugTracker.GetExceptionReportMapper(productRef);
            BugRef bugRef = new BugRef(processedReport, exceptionReportHasher, exceptionReportMapper);
            IEnumerable<TBug> causeBugs = m_BugTracker.Search(bugRef);
            if (!causeBugs.Any())
            {
                TBugDef bugDef = m_BugTracker.CreateBugDef(bugRef);
                m_BugPresenter.PresentNewBug(processedReport, bugDef);
                TBug causeBug = m_BugTracker.CreateBug(bugDef);
                effectBug.AddComment(m_BugPresenter.GetCausalBugCommentFor(causeBug), true);
                causeBug.AddComment(m_BugPresenter.GetEffectBugCommentFor(effectBug), true);
                causeBugs = new[] { causeBug };
            }
            return causeBugs.Select(x => new Tuple<ProcessedReport, TBug>(processedReport, x));
        }
    }
}
